package main

import "fmt"

// ==========方法==========
// 1、golang中的方法是作用在指定的数据类型上的, 自定义的类型, 都可以有方法, 而不仅仅是struct
// 2、自定义类型Type的两个方法method1、method2都可以被变量t1以及指针变量t2调用
//    不管是值传递还是引用传递, 传递给方法的都是变量的副本, 不同的是, 值传递的是值的拷贝, 引用传递的是地址的拷贝
//    - t1调用method1是值传递, 编译器会拷贝一份t1传给t, t1的值等于t的值, t1的地址不等于t的地址
//    - t1调用method2是引用传递, 编译器会将t1的地址拷贝一份传给t, t1的地址等于t的值
//    - t2调用method1是值传递, 编译器会将指针变量t2所指向的数据拷贝一份给t, t2所指向数据的值等于t的值, t2所指向数据的地址不等于t的地址
//    - t2调用method2是引用传递, 编译器会拷贝一份t2传给t, t2的值等于t的值, t2的地址不等于t的地址
//
//    type Type struct {
//        Field1 string
//        Field2 int
//        ...
//        FieldN bool
//    }
//
//    func (t Type) method1() {
//        t.Field = ...
//    }
//
//    func (t *Type) method2() {
//        t.Field = ...
//    }
//
//    t1 := Type{}
//    t2 := &Type{}
//
// 3、只要是方法前面的变量t代表指针变量, 则不管该方法是不是被指针变量所调用, 都会修改变量的值

type People struct {
	Name string
}

func (p People) SayName() {
	fmt.Printf("SayName p 地址: %p\n", &p)
	fmt.Printf("SayName p 值: %v\n", p)
	p.Name = "Max"
}

func (p *People) ModifyName() {
	fmt.Printf("ModifyName p 地址: %p\n", &p)
	fmt.Printf("ModifyName p 值: %p\n", p)
	p.Name = "Kitty"
}

// 给指针类型变量自定义打印格式
func (p *People) String() string {
	return fmt.Sprintf("Name: %v", p.Name)
}

func main() {
	// 1、创建People结构体变量
	p1 := People{"Jason"}
	fmt.Printf("变量p1的地址是: %p\n", &p1)
	fmt.Printf("变量p1的值是: %v\n", p1)
	p1.SayName()
	p1.ModifyName() // 等价于(&p1).ModifyName(), 编译器会自动加上&
	/*
		           0xc00008e1e0
			       --------------
			p1 -> | {Jason}      |
			       --------------

			                  0xc0000ae020
		                      --------------
			ModifyName: p -> | 0xc00008e1e0 |
			                  --------------

			                0xc00008e200
			               --------------
			SayName: p -> | {Jason}      |
			               --------------
	*/

	// 2、创建People结构体变量指针
	p2 := &People{"Adam"}
	fmt.Println(p2) // Name: Adam, 如果不定义String方法, 则返回&{Adam}的形式
	fmt.Printf("变量p2的地址是: %p\n", &p2)
	fmt.Printf("变量p2的值是: %p\n", p2)
	p2.SayName() // 等价于(*p2).SayName(), 编译器会自动加上*
	p2.ModifyName()
	/*
	           0xc0000ae028
		       --------------
		p2 -> | 0xc00008e220 |
		       --------------

		                  0xc0000ae030
	                      --------------
		ModifyName: p -> | 0xc00008e220 |
		                  --------------

		               0xc00008e230
		               --------------
		SayName: p -> | {Adam}       |
		               --------------
	*/
}
